        title  "Vdm Instuction Emulation"
;++
;
; Copyright (c) Microsoft Corporation. All rights reserved. 
;
; You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
; If you do not agree to the terms, do not use the code.
;
;
; Module Name:
;
;    instemul.asm
;
; Abstract:
;
;    This module contains the routines for emulating instructions and
;    faults to a VDM.
;
;--
.386p
        .xlist
include ks386.inc
include i386\kimacro.inc
include mac386.inc
include i386\mi.inc
include callconv.inc
include i386\vdm.inc
include vdmtib.inc
include irqli386.inc
        .list

        extrn   VdmOpcode0f:proc
        extrn   OpcodeNPXV86:proc
        extrn   VdmDispatchIntAck:proc   ;; only OpcodeSti uses this
ifdef VDMDBG
        EXTRNP  _VdmTraceEvent,4
endif
        extrn   CommonDispatchException:proc ;; trap.asm
        extrn   _DbgPrint:proc
        extrn   _KeI386VirtualIntExtensions:dword
        extrn   _MmHighestUserAddress:dword
        EXTRNP  _Ki386GetSelectorParameters,4
        EXTRNP  _Ki386VdmDispatchIo,5
        EXTRNP  _Ki386VdmDispatchStringIo,8
        EXTRNP  _KiDispatchException,5
        EXTRNP  _VdmPrinterStatus,3
        EXTRNP  _VdmPrinterWriteData, 3
        EXTRNP  _VdmClearPMCliTimeStamp, 0
        EXTRNP  _VdmSetPMCliTimeStamp, 1
        extrn   _MmUserProbeAddress:DWORD
        EXTRNP  _VdmFetchULONG,1
        EXTRNP  _Ki386AdjustEsp0,1

        page ,132

ifdef VDMDBG
%out Debugging version
endif

;
;   Force assume into place
;

_PAGE   SEGMENT DWORD PUBLIC 'CODE'
        ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING
_PAGE   ENDS

_TEXT$00   SEGMENT DWORD PUBLIC 'CODE'
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING
_TEXT$00   ENDS

PAGECONST   SEGMENT  DWORD PUBLIC 'DATA'


;
;  Instruction emulation emulates the following instructions.
;  The emulation affects the noted user mode registers.
;
;  In protected mode, the following instructions are emulated in the kernel
;
;    Registers  (E)Flags (E)SP  SS  CS
;       INTnn      X       X     X   X
;       INTO       X       X     X   X
;       CLI        X
;       STI        X
;
;  The following instructions are always emulated by reflection to the
;  Usermode VDM monitor
;
;       INSB
;       INSW
;       OUTSB
;       OUTSW
;       INBimm
;       INWimm
;       OUTBimm
;       OUTWimm
;       INB
;       INW
;       OUTB
;       OUTW
;
;  WARNING What do we do about 32 bit io instructions??


;
;       OpcodeIndex - packed 1st level table to index OpcodeDispatch table
;
        public OpcodeIndex
diBEGIN OpcodeIndex,VDM_INDEX_Invalid
        dtI      0fh, VDM_INDEX_0F
        dtI      26h, VDM_INDEX_ESPrefix
        dtI      2eh, VDM_INDEX_CSPrefix
        dtI      36h, VDM_INDEX_SSPrefix
        dtI      3eh, VDM_INDEX_DSPrefix
        dtI      64h, VDM_INDEX_FSPrefix
        dtI      65h, VDM_INDEX_GSPrefix
        dtI      66h, VDM_INDEX_OPER32Prefix
        dtI      67h, VDM_INDEX_ADDR32Prefix
        dtI      6ch, VDM_INDEX_INSB
        dtI      6dh, VDM_INDEX_INSW
        dtI      6eh, VDM_INDEX_OUTSB
        dtI      6fh, VDM_INDEX_OUTSW
        dtI      9bh, VDM_INDEX_NPX
        dtI      9ch, VDM_INDEX_PUSHF
        dtI      9dh, VDM_INDEX_POPF
        dtI     0cdh, VDM_INDEX_INTnn
        dtI     0ceh, VDM_INDEX_INTO
        dtI     0cfh, VDM_INDEX_IRET
        dtI     0d8h, VDM_INDEX_NPX
        dtI     0d9h, VDM_INDEX_NPX
        dtI     0dah, VDM_INDEX_NPX
        dtI     0dbh, VDM_INDEX_NPX
        dtI     0dch, VDM_INDEX_NPX
        dtI     0ddh, VDM_INDEX_NPX
        dtI     0deh, VDM_INDEX_NPX
        dtI     0dfh, VDM_INDEX_NPX
        dtI     0e4h, VDM_INDEX_INBimm
        dtI     0e5h, VDM_INDEX_INWimm
        dtI     0e6h, VDM_INDEX_OUTBimm
        dtI     0e7h, VDM_INDEX_OUTWimm
        dtI     0ech, VDM_INDEX_INB
        dtI     0edh, VDM_INDEX_INW
        dtI     0eeh, VDM_INDEX_OUTB
        dtI     0efh, VDM_INDEX_OUTW
        dtI     0f0h, VDM_INDEX_LOCKPrefix
        dtI     0f2h, VDM_INDEX_REPNEPrefix
        dtI     0f3h, VDM_INDEX_REPPrefix
        dtI     0f4h, VDM_INDEX_HLT
        dtI     0fah, VDM_INDEX_CLI
        dtI     0fbh, VDM_INDEX_STI
diEND   NUM_OPCODE

;
;       OpcodeDispatch - table of routines used to emulate instructions
;

        public OpcodeDispatch
dtBEGIN OpcodeDispatch,OpcodeInvalid
        dtS     VDM_INDEX_0F          , Opcode0F
        dtS     VDM_INDEX_ESPrefix    , OpcodeESPrefix
        dtS     VDM_INDEX_CSPrefix    , OpcodeCSPrefix
        dtS     VDM_INDEX_SSPrefix    , OpcodeSSPrefix
        dtS     VDM_INDEX_DSPrefix    , OpcodeDSPrefix
        dtS     VDM_INDEX_FSPrefix    , OpcodeFSPrefix
        dtS     VDM_INDEX_GSPrefix    , OpcodeGSPrefix
        dtS     VDM_INDEX_OPER32Prefix, OpcodeOPER32Prefix
        dtS     VDM_INDEX_ADDR32Prefix, OpcodeADDR32Prefix
        dtS     VDM_INDEX_INSB        , OpcodeINSB
        dtS     VDM_INDEX_INSW        , OpcodeINSW
        dtS     VDM_INDEX_OUTSB       , OpcodeOUTSB
        dtS     VDM_INDEX_OUTSW       , OpcodeOUTSW
        dtS     VDM_INDEX_INTnn       , OpcodeINTnn
        dtS     VDM_INDEX_INTO        , OpcodeINTO
        dtS     VDM_INDEX_INBimm      , OpcodeINBimm
        dtS     VDM_INDEX_INWimm      , OpcodeINWimm
        dtS     VDM_INDEX_OUTBimm     , OpcodeOUTBimm
        dtS     VDM_INDEX_OUTWimm     , OpcodeOUTWimm
        dtS     VDM_INDEX_INB         , OpcodeINB
        dtS     VDM_INDEX_INW         , OpcodeINW
        dtS     VDM_INDEX_OUTB        , OpcodeOUTB
        dtS     VDM_INDEX_OUTW        , OpcodeOUTW
        dtS     VDM_INDEX_LOCKPrefix  , OpcodeLOCKPrefix
        dtS     VDM_INDEX_REPNEPrefix , OpcodeREPNEPrefix
        dtS     VDM_INDEX_REPPrefix   , OpcodeREPPrefix
        dtS     VDM_INDEX_CLI         , OpcodeCLI
        dtS     VDM_INDEX_STI         , OpcodeSTI
dtEND   MAX_VDM_INDEX

PAGECONST   ENDS

PAGEDATA   SEGMENT  DWORD PUBLIC 'DATA'

        public  _ExVdmOpcodeDispatchCounts,_ExVdmSegmentNotPresent
_ExVdmOpcodeDispatchCounts dd      MAX_VDM_INDEX dup(0)
_ExVdmSegmentNotPresent    dd      0

PAGEDATA   ENDS

_PAGE   SEGMENT DWORD PUBLIC 'CODE'
        ASSUME  DS:NOTHING, ES:NOTHING, SS:FLAT, FS:NOTHING, GS:NOTHING

        page   ,132
        subttl "Overide Prefix Macro"
;++
;
;   Routine Description:
;
;       This macro generates the code for handling override prefixes
;       The routine name generated is OpcodeXXXXPrefix, where XXXX is
;       the name used in the macro invocation.  The code will set the
;       PREFIX_XXXX bit in the Prefix flags.
;
;   Arguments
;       name = name of prefix
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns
;       user mode Eip advanced
;       eax advanced
;       edx contains next byte of opcode
;
;   NOTE: This routine exits by dispatching through the table again.
;--
opPrefix macro name
        public Opcode&name&Prefix
Opcode&name&Prefix proc

        or      [esi].RiPrefixFlags,PREFIX_&name
        jmp     OpcodeGenericPrefix     ; dispatch to next handler

Opcode&name&Prefix endp
endm

irp prefix, <ES, CS, SS, DS, FS, GS, OPER32, ADDR32, LOCK, REPNE, REP>

        opPrefix prefix

endm

        page   ,132
        subttl "Instruction Emulation Dispatcher"
;++
;
;   Routine Description:
;
;       This routine dispatches to the opcode specific emulation routine,
;       based on the first byte of the opcode.  Two byte opcodes, and prefixes
;       result in another level of dispatching, from the handling routine.
;
;   Arguments:
;
;       [esp+4] = pointer to trap frame
;
;   Returns:
;
;       Nothing
;
;

cPublicProc _Ki386DispatchOpcode,1

        push    ebp
        mov     ebp, [esp+8]
        sub     esp,REGINFOSIZE
        mov     esi, esp                        ; scratch area

        CsToLinearPM [ebp].TsSegCs, doerr       ; initialize reginfo

        mov     edi,[ebp].TsEip                 ; get fault instruction address
        cmp     edi,[esi].RiCsLimit             ; check eip
        ja      doerr

        add     edi,[esi].RiCsBase
        cmp     edi, _MmUserProbeAddress
        ja      doerr

        movzx   ecx,byte ptr [edi]              ; get faulting opcode

        mov     eax,ecx
        and     eax,0F8h                                ; check for npx instr
        cmp     eax,0D8h
        je      do30                                    ; dispatch

        movzx   eax, OpcodeIndex[ecx]
        mov     ebx,1                           ; length count, flags

        ; All handler routines will get the following on entry
        ; ebp -> trap frame
        ; ebx -> prefix flags, instruction length count
        ; ecx -> byte at the faulting address
        ; edx -> pointer to vdm state in DOS arena
        ; interrupts enabled and Irql at APC level
        ; edi -> address of faulting instruction
        ; esi -> reginfo struct
        ; All handler routines will return
        ; EAX = 0 for failure
        ; EAX = 1 for success
if DEVL
        inc     _ExVdmOpcodeDispatchCounts[eax * type _ExVdmOpcodeDispatchCounts]
endif
ifdef VDMDBG
        pushad
        stdCall _VdmTraceEvent, <VDMTR_KERNEL_OP_PM,ecx,0,ebp>
        popad
endif

        call    OpcodeDispatch[eax * type OpcodeDispatch]
do20:
        add     esp,REGINFOSIZE
        pop     ebp
        stdRET  _Ki386DispatchOpcode

doerr:  xor     eax,eax
        jmp     do20

        ;
        ; If we get here, we have executed an NPX instruction in user mode
        ; with the emulator installed.  If the EM bit was not set in CR0, the
        ; app really wanted to execute the instruction for detection purposes.
        ; In this case, we need to clear the TS bit, and restart the instruction.
        ; Otherwise we need to reflect the exception
        ;
do30:
        call OpcodeNPXV86
        jmp  short do20

stdENDP _Ki386DispatchOpcode


        page   ,132
        subttl "Invalid Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine causes a GP fault to be reflected to the vdm
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public OpcodeInvalid
OpcodeInvalid proc
        xor     eax,eax                 ; ret fail
        ret

OpcodeInvalid endp


        page   ,132
        subttl "Generic Prefix Handler"
;++
;
;   Routine Description:
;
;       This routine handles the generic portion of all of the prefixes,
;       and dispatches the next byte of the opcode.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       user mode Eip advanced
;       edx contains next byte of opcode
;

        public OpcodeGenericPrefix
OpcodeGenericPrefix proc

        inc     edi                             ; increment eip
        inc     ebx                             ; increment size
        cmp     bl, 128                         ; set arbitrary inst size limit
        ja      ogperr                          ; in case of pointless prefixes

        mov     eax,edi                         ; current linear address
        sub     eax,[esi].RiCsBase              ; make address eip
        cmp     eax,[esi].RiCsLimit             ; check eip
        ja      ogperr

        cmp     edi, [_MmHighestUserAddress]
        ja      ogperr

        mov     cl,byte ptr [edi]               ; get next opcode

        movzx   eax, OpcodeIndex[ecx]
if DEVL
        inc     _ExVdmOpcodeDispatchCounts[eax * type _ExVdmOpcodeDispatchCounts]
endif
        jmp     OpcodeDispatch[eax * type OpcodeDispatch]

ogperr:
        xor     eax,eax             ; opcode was NOT handled
        ret

OpcodeGenericPrefix endp


        page   ,132
        subttl "0F Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates a 0Fh opcode.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public Opcode0F
Opcode0F proc

        mov     eax,[ebp].TsEip                 ; get fault instruction address
        mov     [esi].RiEip,eax
        mov     [esi].RiTrapFrame,ebp
        mov     [esi].RiPrefixFlags,ebx
        mov     eax,dword ptr [ebp].TsEFlags
        mov     [esi].RiEFlags,eax

        call    VdmOpcode0F                     ; enables interrupts
        test    eax,0FFFFh
        jz      o0f20

        mov     eax,[esi].RiEip
        mov     [ebp].TsEip,eax
        mov     eax,1
o0f20:
        ret

Opcode0F endp

        page   ,132
        subttl "Byte string in Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an INSB opcode.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;
;  WARNING what to do about size override?  ds override?

        public OpcodeINSB
OpcodeINSB proc

        push    ebp                          ; Trap Frame
        push    ebx                          ; size of insb

        movzx   eax,word ptr [ebp].TsSegEs
        shl     eax,16
        ; WARNING no support for 32bit edi
        mov     ax,word ptr [ebp].TsEdi      ; don't support 32bit'ness
        push    eax                          ; address

        xor     eax, eax
        mov     ecx,1
        test    ebx,PREFIX_REP
        jz      @f

        mov     eax, 1
        ; WARNING no support for 32bit ecx
        movzx   ecx,word ptr [ebp].TsEcx
@@:

        push    ecx                          ; number of io ops
        push    TRUE                         ; read op
        push    eax                          ; REP prefix
        push    1                            ; byte op
        movzx   edx,word ptr [ebp].TsEdx
        push    edx                          ; port number
        call    _Ki386VdmDispatchStringIo@32 ; use retval

        ret

OpcodeINSB endp

        page   ,132
        subttl "Word String In Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an INSW opcode.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public OpcodeINSW
OpcodeINSW proc

        push    ebp                             ; Trap frame
        push    ebx                             ; sizeof insw

        movzx   eax,word ptr [ebp].TsSegEs
        shl     eax,16
        ; WARNING no support for 32bit edi
        mov     ax,word ptr [ebp].TsEdi
        push    eax                             ; address

        xor     eax, eax
        mov     ecx,1
        test    ebx,PREFIX_REP
        jz      @f

        mov     eax, 1
        ; WARNING no support for 32bit ecx
        movzx   ecx,word ptr [ebp].TsEcx
@@:
        movzx   edx,word ptr [ebp].TsEdx
        push    ecx                             ; number of io ops
        push    TRUE                            ; read op
        push    eax                             ; REP prefix
        push    2                               ; word size
        push    edx                             ; port number
        call    _Ki386VdmDispatchStringIo@32 ; use retval

        ret

OpcodeINSW endp

        page   ,132
        subttl "Byte String Out Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an OUTSB opcode.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public OpcodeOUTSB
OpcodeOUTSB proc

        push    ebp                           ; Trap Frame
        push    ebx                           ; size of outsb

        movzx   eax,word ptr [ebp].TsSegDs
        shl     eax,16
        ; WARNING don't support 32bit'ness, esi
        mov     ax,word ptr [ebp].TsEsi
        push    eax                           ; address

        xor     eax, eax
        mov     ecx,1
        test    ebx,PREFIX_REP
        jz      @f

        mov     eax, 1
        ; WARNING don't support 32bit'ness ecx
        movzx   ecx,word ptr [ebp].TsEcx
@@:
        movzx   edx,word ptr [ebp].TsEdx
        push    ecx                           ; number of io ops
        push    FALSE                         ; write op
        push    eax                           ; REP prefix
        push    1                             ; byte op
        push    edx                           ; port number
        call    _Ki386VdmDispatchStringIo@32 ; use retval

        ret

OpcodeOUTSB endp

        page   ,132
        subttl "Word String Out Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an OUTSW opcode.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public OpcodeOUTSW
OpcodeOUTSW proc

        push    ebp                               ; Trap Frame
        push    ebx                               ; size of outsb

        movzx   eax,word ptr [ebp].TsSegDs
        shl     eax,16
        ; WARNING don't support 32bit'ness esi
        mov     ax,word ptr [ebp].TsEsi
        push    eax                               ; address

        xor     eax, eax
        mov     ecx,1
        test    ebx,PREFIX_REP
        jz      @f

        mov     eax, 1
        ; WARNING don't support 32bit'ness ecx
        movzx   ecx,word ptr [ebp].TsEcx
@@:
        movzx   edx,word ptr [ebp].TsEdx

        push    ecx                               ; number of io ops
        push    FALSE                             ; write op
        push    eax                               ; REP prefix
        push    2                                 ; byte op
        push    edx                               ; port number
        call    _Ki386VdmDispatchStringIo@32 ; use retval

        ret

OpcodeOUTSW endp

        page   ,132
        subttl "INTnn Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an INTnn opcode.  It retrieves the handler
;       from the IVT, pushes the current cs:ip and flags on the stack,
;       and dispatches to the handler.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       Current CS:IP on user stack
;       RiCs:RiEip -> handler from IVT
;

        public OpcodeINTnn
OpcodeINTnn proc

        mov     eax, ds:FIXED_NTVDMSTATE_LINEAR
        and     eax, (VDM_INTERRUPT_PENDING + VDM_VIRTUAL_INTERRUPTS)
        cmp     eax, (VDM_INTERRUPT_PENDING + VDM_VIRTUAL_INTERRUPTS)
        jnz     short oi10

        call    VdmDispatchIntAck
        jmp     oi99

oi10:
        mov     eax,dword ptr [ebp].TsEFlags
        call    GetVirtualBits                   ; set interrupt flag
        mov     [esi].RiEFlags,eax
        movzx   eax,word ptr [ebp].TsHardwareSegSs
        call    SsToLinear
        test    al,0FFh
        jz      oinerr

        inc     edi                             ; point to int #
        mov     eax,edi                         ; current linear address
        sub     eax,[esi].RiCsBase              ; make address eip
        cmp     eax,[esi].RiCsLimit             ; check eip
        ja      oinerr

        cmp     edi, [_MmHighestUserAddress]
        ja      oinerr

        movzx   ecx,byte ptr [edi]              ; get int #
        inc     eax                             ; inc past end of instruction
        mov     [esi].RiEip,eax                 ; save for pushint's benefit
        call    PushInt                         ; will return retcode in al
        test    al,0FFh
        jz      oinerr                          ; error!

        mov     eax,[esi].RiEsp
        mov     [ebp].TsHardwareEsp,eax
        mov     ax,word ptr [esi].RiSegCs
        or      ax, 7                           ; R3 LDT selectors only
        cmp     ax, 8
        jge     short @f
        test    dword ptr [esi].RiEFlags, EFLAGS_V86_MASK
        jnz     short @f
        mov     ax, KGDT_R3_DATA OR RPL_MASK
@@:     mov     word ptr [ebp].TsSegCs,ax
        mov     eax,[esi].RiEFlags
        push    [ebp].TsEFlags
        mov     [ebp].TsEFlags,eax
        ;
        ; Here we directly enable INT on TrapFrame.  This may break Kei386IOPLAllowed.
        ; Eventually, if we decide to support Kei386IoplAllowed.  We need to make it work
        ; first.  Today, it does NOT work.  We should remove the IOPL allowed stuff.
        ;
        or      dword ptr [ebp].TsEFlags, EFLAGS_INTERRUPT_MASK
        xor     eax, [esp]
        test    eax, EFLAGS_V86_MASK
        pop     eax
        je      short @f
        stdCall _Ki386AdjustEsp0, <ebp>
@@:     mov     eax,[esi].RiEip
        mov     [ebp].TsEip,eax
oi99:
        mov     eax,1
        ret

oinerr:
        xor     eax,eax
        ret


OpcodeINTnn endp

        page   ,132
        subttl "INTO Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an INTO opcode.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public OpcodeINTO
OpcodeINTO proc

        xor     eax,eax
        ret

OpcodeINTO endp


        page   ,132
        subttl "In Byte Immediate Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an in byte immediate opcode.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public OpcodeINBimm
OpcodeINBimm proc

        inc     ebx                             ; length count
        inc     edi
        mov     eax,edi                         ; current linear address
        sub     eax,[esi].RiCsBase              ; make address eip
        cmp     eax,[esi].RiCsLimit             ; check eip
        ja      oibi20

        cmp     edi, [_MmHighestUserAddress]
        ja      oibi20

        movzx   ecx,byte ptr [edi]

; (eax) = inst. size
; read op
; I/O size = 1
; (ecx) = port number

        stdCall   _Ki386VdmDispatchIo, <ecx, 1, TRUE, ebx, ebp>
        ret
oibi20:
        xor     eax, eax                        ; not handled
        ret

OpcodeINBimm endp

        page   ,132
        subttl "Word In Immediate Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an in word immediate opcode.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public OpcodeINWimm
OpcodeINWimm proc

        inc     ebx                             ; length count
        inc     edi
        mov     eax,edi                         ; current linear address
        sub     eax,[esi].RiCsBase              ; make address eip
        cmp     eax,[esi].RiCsLimit             ; check eip
        ja      oiwi20

        cmp     edi, [_MmHighestUserAddress]
        ja      oiwi20

        movzx   ecx,byte ptr [edi]

; TRUE - read op
; 2 - word op
; ecx - port number
        stdCall   _Ki386VdmDispatchIo, <ecx, 2, TRUE, ebx, ebp>
        ret
oiwi20:
        xor     eax, eax                        ; not handled
        ret

OpcodeINWimm endp

        page   ,132
        subttl "Out Byte Immediate Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an invalid opcode.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public OpcodeOUTBimm
OpcodeOUTBimm proc

        inc     ebx                             ; length count
        inc     edi
        mov     eax,edi                         ; current linear address
        sub     eax,[esi].RiCsBase              ; make address eip
        cmp     eax,[esi].RiCsLimit             ; check eip
        ja      oobi20

        cmp     edi, [_MmHighestUserAddress]
        ja      oobi20

        movzx   ecx,byte ptr [edi]

; FALSE - write op
; 1 - byte op
; ecx - port #

        stdCall   _Ki386VdmDispatchIo, <ecx, 1, FALSE, ebx, ebp>
        ret
oobi20:
        xor     eax, eax                        ; not handled
        ret

OpcodeOUTBimm endp

        page   ,132
        subttl "Out Word Immediate Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an out word immediate opcode.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public OpcodeOUTWimm
OpcodeOUTWimm proc

        inc     ebx                             ; length count
        inc     edi
        mov     eax,edi                         ; current linear address
        sub     eax,[esi].RiCsBase              ; make address eip
        cmp     eax,[esi].RiCsLimit             ; check eip
        ja      oowi20

        cmp     edi, [_MmHighestUserAddress]
        ja      oowi20

        movzx   ecx,byte ptr [edi]

; FALSE - write op
; 2 - word op
; ecx - port number
        stdCall   _Ki386VdmDispatchIo, <ecx, 2, FALSE, ebx, ebp>
        ret

oowi20:
        xor     eax, eax                        ; not handled
        ret

OpcodeOUTWimm endp

        page   ,132
        subttl "INB Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an INB opcode.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public OpcodeINB
OpcodeINB proc

        movzx   eax,word ptr [ebp].TsEdx

; TRUE - read op
; 1 - byte op
; eax - port number

        cmp     eax, 3bdh
        jz      oib_prt1
        cmp     eax, 379h
        jz      oib_prt1
        cmp     eax, 279h
        jz      oib_prt1

oib_reflect:
        stdCall   _Ki386VdmDispatchIo, <eax, 1, TRUE, ebx, ebp>
        ret

oib_prt1:
        ; call printer status routine with port number, size, trap frame
        movzx   ebx, bl                     ;clear prefix flags
        push    eax
        stdCall _VdmPrinterStatus, <eax, ebx, ebp>
        or      al,al
        pop     eax
        jz      short oib_reflect
        mov     al, 1
        ret

OpcodeINB endp

        page   ,132
        subttl "INW Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an INW opcode.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public OpcodeINW
OpcodeINW proc

        movzx   eax,word ptr [ebp].TsEdx

; TRUE - read operation
; 2 - word op
; eax - port number
        stdCall   _Ki386VdmDispatchIo, <eax, 2, TRUE, ebx, ebp>
        ret

OpcodeINW endp

        page   ,132
        subttl "OUTB Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an OUTB opcode.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public OpcodeOUTB
OpcodeOUTB proc

        movzx   eax,word ptr [ebp].TsEdx

        cmp     eax, 03BCh
        je      short oob_printerVDD
        cmp     eax, 0378h
        je      short oob_printerVDD
        cmp     eax, 0278h
        jz      short oob_printerVDD

oob_reflect:
; FALSE - write op
; 1 - byte op
; eax - port number
        stdCall   _Ki386VdmDispatchIo, <eax, 1, FALSE, ebx, ebp>
        ret

oob_printerVDD:
        movzx   ebx, bl                   ; instruction size
        push    eax                       ; save port address
        stdCall _VdmPrinterWriteData, <eax, ebx, ebp>
        or      al,al                     ;
        pop     eax
        jz      short oob_reflect
        mov     al, 1
        ret

OpcodeOUTB endp

        page   ,132
        subttl "OUTW Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an OUTW opcode.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public OpcodeOUTW
OpcodeOUTW proc

        movzx   eax,word ptr [ebp].TsEdx

; FALSE - write op
; 2 - word op
; edi - port #
        stdCall   _Ki386VdmDispatchIo, <eax, 2, FALSE, ebx, ebp>
        ret

OpcodeOUTW endp

        page   ,132
        subttl "CLI Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an CLI opcode. It clears the virtual
;       interrupt flag in the VdmTeb.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public OpcodeCLI
OpcodeCLI proc

        mov     eax, ds:FIXED_NTVDMSTATE_LINEAR
        and     eax, (VDM_INTERRUPT_PENDING + VDM_VIRTUAL_INTERRUPTS)
        cmp     eax, (VDM_INTERRUPT_PENDING + VDM_VIRTUAL_INTERRUPTS)
        jnz     short oc50

        call    VdmDispatchIntAck
        jmp     short oc99

oc50:
        mov     eax,[ebp].TsEFlags
        and     eax,NOT EFLAGS_INTERRUPT_MASK
        call    SetVirtualBits
        inc     dword ptr [ebp].TsEip
        stdCall _VdmSetPMCliTimeStamp, <0>
oc99:
        mov     eax,1
        ret

OpcodeCLI endp

        page   ,132
        subttl "STI Opcode Handler"
;++
;
;   Routine Description:
;
;       This routine emulates an STI opcode.  It sets the virtual
;       interrupt flag in the VDM teb.
;
;   Arguments:
;       EBP -> trap frame
;       EBX -> prefix flags, BL = instruction length count
;       ECX -> byte at the faulting address
;       EDX -> pointer to vdm state in DOS arena
;       ESI -> Reginfo struct
;       EDI -> address of faulting instruction
;
;   Returns:
;
;       nothing
;

        public OpcodeSTI
OpcodeSTI proc

        stdCall _VdmClearPMCliTimeStamp
        mov     eax,[ebp].TsEFlags
        or      eax,EFLAGS_INTERRUPT_MASK
        call    SetVirtualBits
        inc     dword ptr [ebp].TsEip
        mov     eax, ds:FIXED_NTVDMSTATE_LINEAR
        test    eax,VDM_INTERRUPT_PENDING
        jz      os10

        call    VdmDispatchIntAck
os10:
        mov     eax,1
        ret

OpcodeSTI endp

        page   ,132
        subttl "Check Vdm Flags"
;++
;
;   Routine Description:
;
;       This routine checks the flags that are going to be used for the
;       dos or windows application.
;
;   Arguments:
;
;       ecx = EFlags to be set
;       esi = address of reg info
;
;   Returns:
;
;       ecx = fixed flags
;

CheckVdmFlags proc

        mov     eax,[esi].RiEFlags
        and     eax,EFLAGS_V86_MASK

        ;
        ; [eax] = V86 mode bit
        ; [ecx] = Flags to be fixed
        ;

        test    eax,EFLAGS_V86_MASK             ; Is V86 Mode?
        jz      short cvf10                     ; No, enable IF

        test    _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS
        jz      short cvf10

        ;
        ; Convert EFLAGS_INTERRUPT_MASK to VIF flags
        ;

        mov     edx, ecx
        and     edx,EFLAGS_INTERRUPT_MASK
        shl     edx,0ah
        or      eax,edx

cvf10:  or      ecx,EFLAGS_INTERRUPT_MASK
cvf20:  and     ecx,NOT (EFLAGS_IOPL_MASK OR EFLAGS_NT_MASK OR EFLAGS_V86_MASK OR EFLAGS_VIF OR EFLAGS_VIP)
        or      ecx,eax                 ; restore original v86 bit
        ret

CheckVdmFlags endp

        page   ,132
        subttl "Get Virtual Interrupt Flag"
;++
;
;   Routine Description:
;
;       This routine correctly gets the VDMs virtual interrupt flag and
;       puts it into an EFlags image to be put on the stack.
;
;   Arguments:
;
;       eax = EFlags value
;
;   Returns:
;
;       eax = EFlags value with correct setting for IF
;
;   Uses:
;       ecx
;
        public GetVirtualBits
GetVirtualBits proc

        push    ebp
        push    edx
        push    ebx
        push    esi
        push    edi


        test    eax, EFLAGS_V86_MASK
        jz      short gvb10

        test    _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS
        jz      short gvb10

        mov     ecx,eax
        and     ecx,EFLAGS_VIF
        shr     ecx,0ah                         ; mov vif to if posn
        and     eax,NOT EFLAGS_INTERRUPT_MASK
        or      eax,ecx

        call    gvbGetFixedStateLinear          ; after return [ecx] = content of 0x417
        and     ecx,VDM_VIRTUAL_AC
        and     eax,NOT EFLAGS_ALIGN_CHECK
        or      eax,ecx
        jmp     short gbvexit

gvb10:  and     eax,NOT EFLAGS_INTERRUPT_MASK
        call    gvbGetFixedStateLinear          ; after return [ecx] = content of 0x417
        and     ecx,VDM_VIRTUAL_INTERRUPTS OR VDM_VIRTUAL_AC
        or      eax,ecx                         ; put virtual int flag into flags
gbvexit:
        or      eax,EFLAGS_IOPL_MASK            ; make it look like a 386

        pop     edi
        pop     esi
        pop     ebx
        pop     edx
        pop     ebp
        ret

gvbGetFixedStateLinear:
        push    eax
        push    offset GetVirtualBits_Handler
        push    PCR[PcExceptionList]
        mov     PCR[PcExceptionList], esp
        mov     ecx, ds:FIXED_NTVDMSTATE_LINEAR

gvbexit1:
        pop     PCR[PcExceptionList]
        add     esp, 4                  ; pop out except handler
        pop     eax
        ret
GetVirtualBits endp

GetVirtualBits_Handler proc
        mov     esp, [esp+8]            ; (esp)-> ExceptionList
        xor     ecx, ecx
        jmp     gvbexit1
GetVirtualBits_Handler endp



        page   ,132
        subttl "Set Virtual Interrupt Flag"
;++
;
;   Routine Description:
;
;       This routine correctly sets the VDMs virtual interrupt flag.
;
;   Arguments:
;
;       eax = EFlags value
;
;   Returns:
;
;       Virtual interrupt flag set
;

SetVirtualBits proc
Flags   equ [ebp - 4]

        ;
        ; IMPORTANT: save ALL the non-volatile registers in case of exception
        ;

        push    ebp
        push    edx
        push    ebx
        push    esi
        push    edi

        push    offset SetVirtualBits_Handler
        push    PCR[PcExceptionList]
        mov     PCR[PcExceptionList], esp
        mov     ebp,esp
        sub     esp,4

        mov     Flags,eax
        lea     edx,ds:FIXED_NTVDMSTATE_LINEAR
        and     eax,EFLAGS_INTERRUPT_MASK ; isolate int flag
        MPLOCK and [edx],NOT VDM_VIRTUAL_INTERRUPTS
        MPLOCK or [edx],eax             ; place virtual int flag value

svb20:
        ; WARNING 32 bit support!
        test    ebx,PREFIX_OPER32
        jz      short svb30             ; 16 bit instr

        mov     eax,Flags
        and     eax,EFLAGS_ALIGN_CHECK
        MPLOCK  and     dword ptr [edx],NOT EFLAGS_ALIGN_CHECK
        MPLOCK  or      [edx],eax
svb30:
        mov     esp,ebp
svbexit:
        pop     PCR[PcExceptionList]    ; Remove handler
        lea     esp, [esp+4]
        pop     edi
        pop     esi
        pop     ebx
        pop     edx
        pop     ebp
        ret
SetVirtualBits endp

SetVirtualBits_Handler proc
        mov     esp, [esp+8]            ; (esp)-> ExceptionList
        jmp     svbexit
SetVirtualBits_Handler endp


        page   ,132
        subttl "Reflect Exception to a Vdm"
;++
;
;   Routine Description:
;
;       This routine reflects an exception to a VDM.  It uses the information
;       in the trap frame to determine what exception to reflect, and updates
;       the trap frame with the new CS, EIP, SS, and SP values
;
;   Arguments:
;
;       ebp -> Trap frame
;       ss:esp + 4 = trap number
;
;   Returns
;
;       Nothing
;
;   Notes:
;       Interrupts are enabled upon entry, Irql is at APC level
;       This routine may not preserve all of the non-volatile registers if
;       a fault occurs.
;
cPublicProc _Ki386VdmReflectException,1

RI      equ     [ebp - REGINFOSIZE]

        ;
        ; First make sure this is for us to handle
        ;

        mov     eax,PCR[PcPrcbData+PbCurrentThread]
        mov     eax,[eax]+ThApcState+AsProcess
        cmp     dword ptr [eax]+PrVdmObjects,0 ; is this a vdm process?
        jne     short @f

        xor     eax, eax                    ; not handled

        stdRET  _Ki386VdmReflectException

@@:
        push    ebp
        mov     ebp,esp
        sub     esp,REGINFOSIZE

        pushad

        lea     esi,ds:FIXED_NTVDMSTATE_LINEAR

        ;
        ; Look to see if the debugger wants exceptions
        ;
        stdCall _VdmFetchULONG, <esi>
        test    eax,VDM_BREAK_EXCEPTIONS
        jz      vredbg                          ; no, check for debug events

        mov     ebx,DBG_STACKFAULT
        cmp     word ptr [ebp + 8],0ch          ; stack fault?
        jz      @f                              ; yes, check dbg flag
        mov     ebx,DBG_GPFAULT
        cmp     word ptr [ebp + 8],0dh          ; gp fault?
        jne     vredbg                          ; no, continue

@@:
        test    eax,VDM_USE_DBG_VDMEVENT
        jnz     vrexc_event
        jmp     vrexcd                          ; reflect the exception to 32

        ;
        ; Look to see if the debugger wants debug events
        ;
vredbg:
        test    eax,VDM_BREAK_DEBUGGER
        jz      vrevdm                          ; no debug events, reflect to vdm

        mov     ebx,DBG_SINGLESTEP
        cmp     word ptr [ebp + 8],1
        jnz     @f
        test    eax,VDM_USE_DBG_VDMEVENT
        jnz     vrexc_event
        jmp     vrexc1

@@:
        mov     ebx,DBG_BREAK
        cmp     word ptr [ebp + 8],3
        jnz     vrevdm
        test    eax,VDM_USE_DBG_VDMEVENT
        jnz     vrexc_event
        jmp     vrexc3

        ;
        ; Reflect the exception to the VDM
        ;
vrevdm:
        mov     esi,[ebp]
        cmp     word ptr [esi].TsSegCs, KGDT_R3_CODE OR RPL_MASK  ; int sim after fault?
        je      vre28
if DEVL
        cmp     word ptr [ebp + 8],11
        jne     @f
        inc     _ExVdmSegmentNotPresent
@@:
endif

if DBG
        CurrentIrql
        cmp      al, APC_LEVEL
        jge      @f
        int      3
@@:
endif

        mov     RI.RiTrapFrame,esi
        mov     eax,[esi].TsHardwareSegSs
        mov     RI.RiSegSs,eax
        mov     eax,[esi].TsHardwareEsp
        mov     RI.RiEsp,eax
        mov     eax,[esi].TsEFlags
        mov     RI.RiEFlags,eax
        mov     eax,[esi].TsEip
        mov     RI.RiEip,eax
        mov     eax,[esi].TsSegCs
        mov     RI.RiSegCs,eax
        lea     esi,RI
        call    CsToLinear                      ; uses eax as selector
        test    al,0FFh
        jz      vrerr

        mov     eax,[esi].RiSegSs
        call    SsToLinear
        test    al,0FFh
        jz      vrerr

        mov     ecx,[ebp + 8]
        call    PushException
        test    al,0FFh
        jz      vrerr

        mov     esi,RI.RiTrapFrame
        mov     eax,RI.RiEsp
        mov     [esi].TsHardwareEsp,eax
        xor     bl, bl                           ; R3 mask. 0 on V86 mode
        test    dword ptr [esi].TsEFlags, EFLAGS_V86_MASK ;
        jnz     @F                               ;
        mov     bl, 7                            ; protected mode, R3 LDT selectors only
@@:
        mov     eax,RI.RiSegSs
        or      al, bl
        mov     [esi].TsHardwareSegSs,eax
        mov     eax,RI.RiEFlags
        push    [esi].TsEFlags
        mov     [esi].TsEFlags,eax
        xor     eax, [esp]
        test    eax, EFLAGS_V86_MASK
        pop     eax
        je      @f
        stdCall _Ki386AdjustEsp0, <esi>
@@:

        mov     eax,RI.RiSegCs
        or      al, bl
        cmp     eax, 8
        jae     short @f
        test    dword ptr [esi].TsEFlags, EFLAGS_V86_MASK ;
        jnz     short @f                               ;
        mov     eax, KGDT_R3_CODE OR RPL_MASK
@@:     mov     [esi].TsSegCs,eax
        mov     eax,RI.RiEip
        mov     [esi].TsEip,eax
        cmp     word ptr [ebp + 8],1
        jne     vre28
        and     dword ptr [esi].TsEFlags, NOT EFLAGS_TF_MASK

vre28:
        popad
        mov     eax,1                           ; handled

vre30:
        mov     esp,ebp
        pop     ebp
        stdRET  _Ki386VdmReflectException

vrerr:
        popad
        xor     eax,eax
        jmp     vre30

vrexc1:
        mov     eax, [ebp]
        and     dword ptr [eax]+TsEflags, not EFLAGS_TF_MASK
        mov     eax, [ebp]+TsEip        ; (eax)-> faulting instruction
        stdCall _VdmDispatchException <[ebp],STATUS_SINGLE_STEP,eax,0,0,0,0>
        jmp     vre28

vrexc3:
        mov     eax,BREAKPOINT_BREAK
        mov     ebx, [ebp]
        mov     ebx, [ebx]+TsEip
        dec     ebx                     ; (eax)-> int3 instruction
        stdCall _VdmDispatchException <[ebp],STATUS_BREAKPOINT,ebx,3,eax,ecx,edx>
        jmp     vre28

vrexcd:
        mov     eax, [ebp]
        mov     eax, [eax]+TsEip
        stdCall _VdmDispatchException <[ebp],STATUS_ACCESS_VIOLATION,eax,2,0,-1,0>
        jmp     vre28

vrexc_event:
        mov     eax, [ebp]
        cmp     ebx, DBG_SINGLESTEP
        jnz     vrexc_event2
        and     dword ptr [eax]+TsEflags, not EFLAGS_TF_MASK
vrexc_event2:
        mov     eax, [eax]+TsEip
        stdCall _VdmDispatchException <[ebp],STATUS_VDM_EVENT,eax,1,ebx,0,0>
        jmp     vre28


stdENDP _Ki386VdmReflectException


        page   ,132
        subttl "Reflect Segment Not Present Exception to a Vdm"
;++
;
;   Routine Description:
;
;       This routine reflects an TRAP B to a VDM.  It uses the information
;       in the trap frame to determine what exception to reflect, and updates
;       the trap frame with the new CS, EIP, SS, and SP values
;
;   Arguments:
;
;       ebp -> Trap frame
;
;   Returns
;
;       0 is returned if the reflection fails.
;

cPublicProc _Ki386VdmSegmentNotPresent,0

        mov     edi,PCR[PcTeb]
        mov     ecx,VDM_FAULT_HANDLER_SIZE * 0Bh

        ;
        ; Set up an exception handler in case we fault
        ; during the user-space accesses below.
        ;

        push    ebp
        push    offset FLAT:VdmSegNotPres_ExceptionHandler
                                        ; set up exception registration record
        push    PCR[PcExceptionList]
        mov     PCR[PcExceptionList], esp

        mov     edi,[edi].TeVdm
        xor     ebx, ebx
        cmp     edi, _MmUserProbeAddress     ; probe the TeVdm
        jae     short reflect

        lea     esi,[edi].VtDpmiInfo         ; (esi)->dpmi info struct
        mov     edi, [edi].VtFaultTable      ;
        lea     edi,[edi+ecx]                ; (edi)->FaultHandler
        cmp     edi, _MmUserProbeAddress     ; probe the table address
        jae     short reflect

        cmp     word ptr [esi].VpLockCount, 0 ; switching stacks?
        jz      short seg_not_pres           ; yes, we can handle it
                                             ; no, let normal code check
                                             ; for stack faults

reflect:

        ;
        ; WARNING: Here we directly unlink the exception handler from the
        ; exception registration chain.  NO unwind is performed.
        ;

        pop     PCR[PcExceptionList]

        add     esp, 4                  ; pop out except handler
        pop     ebp

        ;
        ; Reflect the failure (or exception) back to the usermode ntvdm
        ; to handle.
        ;

        pop     eax                          ; (eax) = return addr
        push    0bh
        push    eax
        jmp     _Ki386VdmReflectException

reflect_1:

        add     esp, REGINFOSIZE + 4            ; plus the "push esi" 4 bytes
        jmp     short reflect

seg_not_pres:
if DEVL
        inc     _ExVdmSegmentNotPresent
endif
        inc     word ptr [esi].VpLockCount

        ;
        ; (esi)->dpmi info struct
        ; (edi)->FaultHandler
        ; (ebp)->TrapFrame
        ;
        ; save stuff just like SwitchToHandlerStack does
        ;

        mov     eax, [ebp].TsEip
        mov     [esi].VpSaveEip, eax
        mov     eax, [ebp].TsHardwareEsp
        mov     [esi].VpSaveEsp, eax
        mov     ax, [ebp].TsHardwareSegSs
        mov     [esi].VpSaveSsSelector, ax

        movzx   eax,word ptr [esi].VpSsSelector ; (eax) = PM stack selector
        sub     esp, REGINFOSIZE             ; allocate reginfo table on stack
        push    esi                          ; save dpmi info
        mov     esi, esp
        add     esi, 4                       ; (esi)->RegInfo
        mov     ecx, dword ptr [ebp].TsEFlags
        mov     [esi].RiEFlags,ecx           ; initialize the  reginfo table
        call    SsToLinear                   ; with eax and esi
        test    al,0FFh                      ; is al == 0?
        jz      short reflect_1              ; yes, failed

        mov     ebx, [esi].RiSsBase          ; (ebx) = Base of PM Stack
        pop     esi                          ; (esi)->dpmi info
        add     esp, REGINFOSIZE             ; remove RegInfo from stack
        cmp     ebx, _MmUserProbeAddress     ; probe the PM stack base addr
        jae     short reflect                ;   make sure it is not Kmode addr

        mov     eax, [ebp].TsEFlags
        call    GetVirtualBits               ; (eax) = app's eflags
        push    esi
        mov     edx, 0fe0h                   ; dpmistack offset (per win31)
        test    word ptr [esi].VpFlags, 1    ; 32-bit frame?
        jz      short @f

        sub     edx, 8 * 4
        add     edx, ebx
        mov     esi, [ebp].TsHardwareEsp
        mov     ecx, [ebp].TsHardwareSegSs
        mov     [edx + 20], eax              ; push flags
        mov     [edx + 24], esi              ; put esp on new stack
        mov     [edx + 28], ecx              ; put ss on new stack
        mov     ecx, [ebp].TsSegCs
        mov     eax, [ebp].TsEip
        mov     esi, [ebp].TsErrCode
        mov     [edx + 16], ecx              ; push cs
        mov     [edx + 12], eax              ; push ip
        mov     [edx + 8], esi               ; push error code
        pop     esi
        mov     ecx, [esi].VpDosxFaultIretD
        mov     eax, ecx
        shr     eax, 16
        and     ecx, 0ffffh
        mov     [edx + 4], eax               ; push fault iret seg
        mov     [edx], ecx                   ; push fault iret offset
        jmp     short vsnp_update
@@:
        sub     edx, 8 * 2
        add     edx, ebx
        mov     esi, [ebp].TsHardwareEsp
        mov     ecx, [ebp].TsHardwareSegSs
        mov     [edx + 10], ax               ; push flags
        mov     [edx + 12], si               ; put esp on new stack
        mov     [edx + 14], cx               ; put ss on new stack
        mov     ecx, [ebp].TsSegCs
        mov     eax, [ebp].TsEip
        mov     esi, [ebp].TsErrCode
        mov     [edx + 8], cx                ; push cs
        mov     [edx + 6], ax                ; push ip
        mov     [edx + 4], si                ; push error code
        pop     esi
        mov     ecx, [esi].VpDosxFaultIret
        mov     eax, ecx
        shr     eax, 16
        mov     [edx + 2], ax                ; push fault iret seg
        mov     [edx], cx                    ; push fault iret offset

vsnp_update:
        mov     eax,[edi].VfEip
        sub     edx, ebx
        mov     cx, word ptr [edi].VfCsSelector
        mov     bx, word ptr [esi].VpSsSelector
        test    dword ptr [edi].VfFlags, VDM_INT_INT_GATE
        jz      short @f

        lea     esi,ds:FIXED_NTVDMSTATE_LINEAR
        MPLOCK and      [esi],NOT VDM_VIRTUAL_INTERRUPTS
        and     dword ptr [ebp].TsEflags, 0FFF7FFFFH ; clear VIF
@@:
        or      cx, 7                       ; R3 LDT selectors only
        or      bx, 7                       ; R3 LDT selectors only
        test    dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
        jnz     short @f

        cmp     cx, 8
        jge     short @f

        mov     cx, KGDT_R3_CODE OR RPL_MASK
@@:     mov     [ebp].TsSegCs, cx
        mov     [ebp].TsEip, eax
        mov     [ebp].TsHardwareEsp,edx
        mov     [ebp].TsHardwareSegSs,bx

        ;
        ; WARNING: Here we directly unlink the exception handler from the
        ; exception registration chain.  NO unwind is performed.
        ;

        pop     PCR[PcExceptionList]

        add     esp, 4                  ; pop out except handler
        pop     ebp

        mov     eax, 1
        stdRET    _Ki386VdmSegmentNotPresent


stdENDP _Ki386VdmSegmentNotPresent

        ;
        ;   Error and exception blocks for Ki386VdmSegmentNoPresent
        ;

VdmSegNotPres_ExceptionHandler proc
        ;
        ; WARNING: Here we directly unlink the exception handler from the
        ; exception registration chain.  NO unwind is performed.
        ;

        mov     esp, [esp+8]            ; (esp)-> ExceptionList
        jmp     reflect
VdmSegNotPres_ExceptionHandler endp
	

        page   ,132
        subttl "Dispatch UserMode Exception to a Vdm"
;++
;
;   Routine Description:
;
;   Dispatches exception for vdm from in the kernel, by invoking
;   CommonDispatchException.
;
;   Arguments: See CommonDispatchException for parameter description
;
;   VOID
;   VdmDispatchException(
;        PKTRAP_FRAME TrapFrame,
;        NTSTATUS     ExcepCode,
;        PVOID        ExcepAddr,
;        ULONG        NumParms,
;        ULONG        Parm1,
;        ULONG        Parm2,
;        ULONG        Parm3
;        )
;
;   Returns
;
;       Nothing
;
;   Notes:
;
;       This routine may not preserve all of the non-volatile registers if
;       a fault occurs.
;
cPublicProc _VdmDispatchException,7

TrapFrame equ [ebp+8]
ExcepCode equ [ebp+12]
ExcepAddr equ [ebp+16]
NumParms  equ [ebp+20]
Parm1     equ [ebp+24]
Parm2     equ [ebp+28]
Parm3     equ [ebp+32]

        push    ebp
        mov     ebp,esp
        pushad

        LowerIrql 0                 ; lower irql to 0
                                    ; allow APCs and debuggers in!

        mov    eax, ExcepCode
        mov    ebx, ExcepAddr
        mov    ecx, NumParms
        mov    edx, Parm1
        mov    esi, Parm2
        mov    edi, Parm3
        mov    ebp, TrapFrame
        call   CommonDispatchException

        popad
        pop    ebp

        stdRET  _VdmDispatchException

stdENDP _VdmDispatchException




        page   ,132
        subttl "Push Interrupt frame on user stack"
;++
;
;   Routine Description:
;
;       This routine pushes an interrupt frame on the user stack
;
;   Arguments:
;
;       ecx = interrupt #
;       esi = address of reg info
;   Returns:
;
;       interrupt frame pushed on stack
;       reg info updated
;
        public PushInt
PushInt proc

        push    ebx
        push    edi

;
; Handle dispatching interrupts directly to the handler, rather than
; to the dos extender
;
        ;
        ; Get the information on the interrupt handler
        ;
        .errnz (VDM_INTERRUPT_HANDLER_SIZE - 8)
        mov     eax,PCR[PcTeb]

        ;
        ; Set up an exception handler in case we fault
        ; during the user-space accesses below.
        ; Note, we must preserve esi if exception does occur, all non-volatile registers are destroyed
        ;

        push    esi
        push    ebp
        push    offset FLAT:PushIntExceptionHandler     ; set up exception registration record
        push    PCR[PcExceptionList]
        mov     PCR[PcExceptionList], esp


        mov     eax,[eax].TbVdm
        cmp     eax, _MmUserProbeAddress
        jae     pierr

        mov     eax, [eax].VtInterruptTable
        lea     eax,[eax + ecx*8]
        cmp     eax, _MmUserProbeAddress
        jae     pierr

        ;
        ; Get SP
        ;
        mov     edi,[ebp].TsHardwareEsp
        test    [esi].RiSsFlags,SEL_TYPE_BIG
        jnz     short @f

        movzx   edi,di                          ; zero high bits for 64k stack

        ;
        ; Update SP
        ;
@@:     test    [eax].ViFlags,dword ptr VDM_INT_32
        jz      short @f

        ;
        ; 32 bit iret frame
        ;
        cmp     edi,12                          ; enough space on stack?
        jb      pierr                           ; no, go fault

        sub     edi,12
        mov     [esi].RiEsp,edi
        jmp     short pi130

        ;
        ; 16 bit iret frame
        ;
@@:     cmp     edi,6                           ; enough space on stack?
        jb      pierr                           ; no, go fault

        sub     edi,6
        mov     [esi].RiEsp,edi

        ;
        ; Check limit
        ;
pi130:  test    [esi].RiSsFlags,SEL_TYPE_ED
        jz      short pi140

        ;
        ; Expand down, Sp must be above limit
        ;
        cmp     edi,[esi].RiSsLimit
        jna     pierr

        jmp     short pi150

        ;
        ; Normal, Sp must be below limit
        ;
pi140:  cmp     edi,[esi].RiSsLimit
        jnb     pierr

        ;
        ; Get base of ss
        ;
pi150:  mov     ebx,[esi].RiSsBase
        test    [eax].ViFlags,dword ptr VDM_INT_32
        jz      short pi160

        ;
        ; "push" 32 bit iret frame
        ;
        mov     edx,[esi].RiEip
        mov     [edi + ebx],edx
        mov     dx,word ptr [ebp].TsSegCs
        mov     [edi + ebx] + 4,edx
        push    eax
        mov     eax,[esi].RiEFlags
        call    GetVirtualBits

        mov     [edi + ebx] + 8,eax
        pop     eax
        jmp     short pi170

        ;
        ; push 16 bit iret frame
        ;
pi160:  mov     dx,word ptr [esi].RiEip
        mov     [edi + ebx],dx
        mov     dx,word ptr [ebp].TsSegCs
        mov     [edi + ebx] + 2,dx
        push    eax
        mov     eax,[esi].RiEFlags
        call    GetVirtualBits

        mov     [edi + ebx] + 4,ax
        pop     eax

        ;
        ; Update CS and IP
        ;
pi170:  mov     ebx,eax                                 ; save int info
        mov     dx,[eax].ViCsSelector
        mov     word ptr [esi].RiSegCs,dx
        mov     edx,[eax].ViEip
        mov     [esi].RiEip,edx

        movzx   eax, word ptr [esi].RiSegCs
        call    CsToLinear                      ; uses eax as selector

        test    al,0ffh
        jnz     short pi175

        ;
        ; Check for destination not present
        ;
        test    [esi].RiCsFlags,SEL_TYPE_NP
        jz      pierr

        mov     al,0ffh                         ; succeeded
        jmp     short pi180

        ;
        ; Check handler address
        ;
pi175:  mov     edx,[esi].RiEip
        cmp     edx,[esi].RiCsLimit
        jnb     short pierr

        ;
        ; Turn off the trap flag
        ;
pi180:  and     [esi].RiEFlags,NOT EFLAGS_TF_MASK

        ;
        ; Turn off virtual interrupts if necessary
        ;
        test    [ebx].ViFlags,dword ptr VDM_INT_INT_GATE
        ; n.b. We know al is non-zero, because we succeeded in cstolinear
        jz      short pi80

pi75:   lea     ebx,ds:FIXED_NTVDMSTATE_LINEAR
        MPLOCK and [ebx], NOT EFLAGS_INTERRUPT_MASK

pi80:   and     [esi].RiEFlags,NOT (EFLAGS_IOPL_MASK OR EFLAGS_NT_MASK OR EFLAGS_V86_MASK)
        or      [esi].RiEFlags,EFLAGS_INTERRUPT_MASK

pi90:

        ;
        ; WARNING: Here we directly unlink the exception handler from the
        ; exception registration chain.  NO unwind is performed.
        ;

        pop     PCR[PcExceptionList]
        add     esp, 4                  ; pop out except handler
        pop     ebp
        pop     esi

        pop     edi
        pop     ebx
        ret

pierr:  xor     eax,eax
        jmp     short pi90

PushInt endp

PushIntExceptionHandler proc
        mov     esp, [esp+8]            ; (esp)-> ExceptionList
        xor     eax,eax
        jmp     pi90
PushIntExceptionHandler endp	

        page   ,132
        subttl "Convert CS Segment or selector to linear address"
;++
;
;   Routine Description:
;
;       Convert CS segment or selector to linear address as appropriate
;       for the current user mode processor mode.
;
;   Arguments:
;
;       esi = reg info
;
;   Returns:
;
;       reg info updated
;
        public CsToLinear
CsToLinear proc

        test    [esi].RiEFlags,EFLAGS_V86_MASK
        jz      ctl10

        shl     eax,4
        mov     [esi].RiCsBase,eax
        mov     [esi].RiCsLimit,0FFFFh
        mov     [esi].RiCsFlags,0
        mov     eax,1
        ret


ctl10:
        push    edx                             ; WARNING volatile regs!!!
        lea     edx,[esi].RiCsLimit
        push    edx
        lea     edx,[esi].RiCsBase
        push    edx
        lea     edx,[esi].RiCsFlags
        push    edx
        push    eax                             ; push selector

        call    _Ki386GetSelectorParameters@16
        pop     edx

        or      al,al
        jz      ctlerr

        test    [esi].RiCsFlags,SEL_TYPE_EXECUTE
        jz      ctlerr

        test    [esi].RiCsFlags,SEL_TYPE_2GIG
        jz      ctl30

        ; Correct limit value for granularity
        shl     [esi].RiCsLimit,12
        or      [esi].RiCsLimit,0FFFh
ctl30:
        mov     eax,1
        ret

ctlerr: xor     eax,eax
        ret

CsToLinear endp


        page   ,132
        subttl "Verify that EIP is still valid"
;++
;
;   Routine Description:
;
;       Verify that Eip is still valid and put it into the trap frame
;
;   Arguments:
;
;       esi = address of reg info
;
;   Returns:
;
;
        public CheckEip
CheckEip proc
        mov     eax,[esi].RiEip
        test    [esi].RiEFlags,EFLAGS_V86_MASK
        jz      ce20

        and     eax,[esi].RiCsLimit
        mov     [esi].RiEip,eax
        jmp     ce40

ce20:   cmp     eax,[esi].RiCsLimit
        ja      ceerr
ce40:   mov     eax,1
ce50:   ret

ceerr:  xor     eax,eax
        jmp     ce50

CheckEip endp

        page   ,132
        subttl "Convert Ss Segment or selector to linear address"
;++
;
;   Routine Description:
;
;       Convert Ss segment or selector to linear address as appropriate
;       for the current user mode processor mode.
;
;   Arguments:
;
;       eax = selector to convert
;       esi = address of reg info
;
;   Returns:
;
;       reg info updated
;
        public SsToLinear
SsToLinear proc

        test    [esi].RiEFlags,EFLAGS_V86_MASK
        jz      stl10

        shl     eax,4
        mov     [esi].RiSsBase,eax
        mov     [esi].RiSsLimit,0FFFFh
        mov     [esi].RiSsFlags,0
        mov     eax,1
        ret

stl10:  push    ecx
        lea     ecx,[esi].RiSsLimit
        push    ecx
        lea     ecx,[esi].RiSsBase
        push    ecx
        lea     ecx,[esi].RiSsFlags
        push    ecx
        push    eax                             ;selector

        call    _Ki386GetSelectorParameters@16
        pop     ecx

        or      al,al
        jz      stlerr

        test    [esi].RiSsFlags,SEL_TYPE_WRITE
        jz      stlerr

        test    [esi].RiSsFlags,SEL_TYPE_2GIG
        jz      stl30

        ; Correct limit value for granularity

        mov     eax,[esi].RiSsLimit
        shl     eax,12
        or      eax,0FFFh
        mov     [esi].RiSsLimit,eax
stl30:
        mov     eax,1
stl40:  ret

stlerr: xor     eax,eax
        jmp     stl40

SsToLinear endp

        page   ,132
        subttl "Verify that Esp is still valid"
;++
;
;   Routine Description:
;
;       Verify that Esp is still valid
;
;   Arguments:
;
;       ecx = # of bytes needed for stack frame
;       esi = address of reg info
;
;   Returns:
;
;
;
        public CheckEsp
CheckEsp proc
        mov     eax,[esi].RiEsp
        test    [esi].RiEFlags,EFLAGS_V86_MASK
        jz      cs20

        and     eax,[esi].RiSsLimit
        mov     [esi].RiEsp,eax
        jmp     cs40

cs20:   test    [esi].RiSsFlags,SEL_TYPE_BIG
        jnz     cs25

        and     eax,0FFFFh                      ; only use 16 bit for 16 bit
cs25:
        cmp     ecx, eax                        ; StackOffset > SP?
        ja      cserr                           ; yes error
        dec     eax                             ; make limit checks work
        test    [esi].RiSsFlags,SEL_TYPE_ED     ; Expand down?
        jz      cs30                            ; jif no

;
;       Expand Down
;
        sub     eax, ecx                        ; New SP
        cmp     eax,[esi].RiSsLimit             ; NewSp < Limit?
        jb      cserr
        jmp     cs40

;
;       Not Expand Down
;
cs30:   cmp     eax,[esi].RiSsLimit
        ja      cserr

cs40:   mov     eax,1
cs50:   ret


cserr:  xor     eax,eax
        jmp     cs50

CheckEsp endp

        page   ,132
        subttl "Switch to protected mode interrupt stack"
;++
;
;   Routine Description:
;
;       Switch to protected mode interrupt handler stack
;
;   Arguments:
;
;       ecx = interrupt number
;       esi = address of reg info
;       edi = address of PM Stack info
;
;   Returns:
;
;       reg info updated
;
        public SwitchToHandlerStack
SwitchToHandlerStack proc

;
; We must preserve non-volatile registers across exception
;

        push    ebx
        push    esi
        push    edi

;
; Install exception handler
;

        push    ebp
        push    offset SwitchToHandlerStack_fault ; Set Handler address
        push    PCR[PcExceptionList]        ; Set next pointer
        mov     PCR[PcExceptionList],esp    ; Link us on

        cmp     word ptr [edi].VpLockCount, 0   ; already switched?
        jnz     short @f                        ; yes

        mov     eax, [esi].RiEip
        mov     [edi].VpSaveEip, eax
        mov     eax, [esi].RiEsp
        mov     [edi].VpSaveEsp, eax
        mov     eax, [esi].RiSegSs
        mov     [edi].VpSaveSsSelector, ax

        movzx   eax,word ptr [edi].VpSsSelector

        pop     PCR[PcExceptionList]        ; Remove our exception handle
        add     esp, 4                      ; clear stack
        pop     ebp

        mov     [esi].RiSegSs,eax
        mov     dword ptr [esi].RiEsp,1000h     ; dpmi stack offset

        movzx   eax, word ptr [esi].RiSegSs
        push    ecx
        call    SsToLinear                      ; compute new base
        pop     ecx
        test    al,0FFh
        jz      short shserr

        push    ebp
        push    offset SwitchToHandlerStack_fault ; Set Handler address
        push    PCR[PcExceptionList]        ; Set next pointer
        mov     PCR[PcExceptionList],esp    ; Link us on

@@:
        inc     word ptr [edi].VpLockCount      ; maintain lock count

        pop     PCR[PcExceptionList]        ; Remove our exception handle
        add     esp, 4                      ; clear stack
        pop     ebp

        mov     eax,1
        jmp     short shsexit

shserr:
        xor     eax,eax
shsexit:
        pop     edi
        pop     esi
        pop     ebx
        ret
SwitchToHandlerStack endp

SwitchToHandlerStack_fault proc
        mov     esp, [esp+8]
        pop     PCR[PcExceptionList]        ; Remove our exception handle
        add     esp, 4                      ; clear stack
        pop     ebp
        jmp     short shserr
SwitchToHandlerStack_fault endp


        page   ,132
        subttl "Get protected mode interrupt handler address"
;++
;
;   Routine Description:
;
;       Get the address of the interrupt handler for the specified interrupt
;
;   Arguments:
;
;       ecx = interrupt number
;       esi = address of reg info
;
;   Returns:
;
;       reg info updated
;
        public GetHandlerAddress
GetHandlerAddress proc

        push    ebp
        push    ebx
        push    esi
        push    edi
        push    ecx
        push    edx

        push    offset GetHandlerAddress_fault  ; Set Handler address
        push    PCR[PcExceptionList]            ; Set next pointer
        mov     PCR[PcExceptionList],esp        ; Link us on

        mov     eax,VDM_FAULT_HANDLER_SIZE
        mul     ecx
        mov     edi,PCR[PcTeb]
        mov     edi,[edi].TeVdm
        cmp     edi, _MmUserProbeAddress        ; Probe the VMD structure
        jae     short GetHandlerAddress_fault_resume

        mov     edi,[edi].VtFaultTable
        cmp     edi, _MmUserProbeAddress
        jae     short GetHandlerAddress_fault_resume

        movzx   ecx,word ptr [edi + eax].VfCsSelector
        mov     [esi].RiSegCs,ecx
        mov     ecx,[edi + eax].VfEip
        mov     [esi].RiEip,ecx
        mov     eax,1

        jmp     short GetHandlerAddress_Exit

GetHandlerAddress_fault_resume:
        xor     eax, eax
GethandlerAddress_Exit:
        pop     PCR[PcExceptionList]        ; Remove our exception handle

        add     esp, 4                      ; clear stack

        pop     edx
        pop     ecx
        pop     edi
        pop     esi
        pop     ebx
        pop     ebp
        ret
	
GetHandlerAddress endp

GetHandlerAddress_fault proc
        mov     esp, [esp+8]
        jmp     short GetHandlerAddress_fault_resume
GetHandlerAddress_fault endp

        page   ,132
        subttl "Push processor exception"
;++
;
;   Routine Description:
;
;       Update the stack and registers to emulate the specified exception
;
;   Arguments:
;
;       ecx = interrupt number
;       esi = address of reg info
;
;   Returns:
;
;       reg info updated
;
        public PushException
PushException Proc

        push    ebx
        push    edi
        push    esi

        test    [esi].RiEflags,EFLAGS_V86_MASK
        jz      pe40

;
; Push V86 mode exception
;
        cmp     ecx, 7                  ; device not available fault
        ja      peerr                   ; per win3.1, no exceptions
                                        ; above 7 for v86 mode
        mov     edx,[esi].RiEsp
        mov     ebx,[esi].RiSsBase
        and     edx,0FFFFh              ; only use a 16 bit sp
        sub     dx,2
        mov     eax,[esi].RiEFlags
        push    ecx
        call    GetVirtualBits
        pop     ecx
;
; Install exception handler
;
        push    ebp
        push    esp                         ; Pass current Esp to handler
        push    offset perr_fault           ; Set Handler address
        push    PCR[PcExceptionList]        ; Set next pointer
        mov     PCR[PcExceptionList],esp    ; Link us on

        mov     [ebx+edx],ax            ; push flags
        sub     dx,2
        mov     ax,word ptr [esi].RiSegCs
        mov     [ebx+edx],ax            ; push cs
        sub     dx,2
        mov     ax,word ptr [esi].RiEip
        mov     [ebx+edx],ax            ; push ip

        mov     eax,[ecx*4]             ; get new cs:ip value
        pop     PCR[PcExceptionList]        ; Remove our exception handle
        add     esp, 8                      ; clear stack
        pop     ebp

        push    eax
        movzx   eax,ax
        mov     [esi].RiEip,eax
        pop     eax
        shr     eax,16
        mov     [esi].RiSegCs,eax
        mov     word ptr [esi].RiEsp,dx
        jmp     pe60

;
; Push PM exception
;
pe40:
        push    [esi].RiEsp                     ; save for stack frame
        push    [esi].RiSegSs

;
; Install exception handler
;
        push    ebp
        push    esp                         ; Pass current Esp to handler
        push    offset perr1_fault          ; Set Handler address
        push    PCR[PcExceptionList]        ; Set next pointer
        mov     PCR[PcExceptionList],esp    ; Link us on

        mov     edi,PCR[PcTeb]
        mov     edi, [edi].TeVdm


        pop     PCR[PcExceptionList]        ; Remove our exception handle
        add     esp, 8                      ; clear stack
        pop     ebp

        cmp     edi, _MmUserProbeAddress
        jae     peerr1
        lea     edi,[edi].VtDpmiInfo
        call    SwitchToHandlerStack
        test    al,0FFh
        jz      peerr1                          ; pop off stack and exit

        sub     [esi].RiEsp, 20h                ; win31 undocumented feature

        mov     ebx,[esi].RiSsBase
        mov     edx,[esi].RiEsp
        test    [esi].RiSsFlags,SEL_TYPE_BIG
        jnz     short @f
        movzx   edx,dx                          ; zero high bits for 64k stack
@@:

;
; Install exception handler
;
        push    ebp
        push    esp                         ; Pass current Esp to handler
        push    offset perr1_fault           ; Set Handler address
        push    PCR[PcExceptionList]        ; Set next pointer
        mov     PCR[PcExceptionList],esp    ; Link us on

        test    word ptr [edi].VpFlags, 1   ; 32 bit app?

        pop     PCR[PcExceptionList]        ; Remove our exception handle

        lea     esp, [esp+8]                ; clear stack
        pop     ebp

        jnz     pe45                        ; yes

;
;       push 16-bit frame
;
        push    ecx
        mov     ecx, 8*2                        ; room for 8 words?
        call    CheckEsp
        pop     ecx
        test    al,0FFh
        jz      peerr1                          ; pop off stack and exit

        sub     edx,8*2
        mov     [esi].RiEsp,edx

;
; Install exception handler
;
        push    ebp
        push    esp                         ; Pass current Esp to handler
        push    offset perr1_fault           ; Set Handler address
        push    PCR[PcExceptionList]        ; Set next pointer
        mov     PCR[PcExceptionList],esp    ; Link us on

        mov     eax, [esp+4*4]
        mov     [ebx+edx+14], ax
        mov     eax, [esp+4*5]
        mov     [ebx+edx+12], ax

        pop     PCR[PcExceptionList]        ; Remove our exception handle
        lea     esp, [esp+8]                ; clear stack
        pop     ebp
        lea     esp, [esp+8]                ; clear stack

        mov     eax,[esi].RiEFlags
        push    ecx
        call    GetVirtualBits
        pop     ecx

;
; Install exception handler
;
        push    ebp
        push    esp                         ; Pass current Esp to handler
        push    offset perr_fault           ; Set Handler address
        push    PCR[PcExceptionList]        ; Set next pointer
        mov     PCR[PcExceptionList],esp    ; Link us on

        mov     [ebx+edx+10],ax                 ; push flags
        movzx   eax,word ptr [esi].RiSegCs
        mov     [ebx+edx+8],ax                  ; push cs
        mov     eax,[esi].RiEip
        mov     [ebx+edx+6],ax                  ; push ip
        mov     eax,RI.RiTrapFrame
        mov     eax,[eax].TsErrCode
        mov     [ebx+edx+4],ax                  ; push error code
        mov     eax,[edi].VpDosxFaultIret
        mov     [ebx+edx],eax                   ; push iret address

        pop     PCR[PcExceptionList]        ; Remove our exception handle

        add     esp, 8                      ; clear stack
        pop     ebp

        jmp     pe50
pe45:
;
;       push 32-bit frame
;
        push    ecx
        mov     ecx, 8*4                        ; room for 8 dwords?
        call    CheckEsp
        pop     ecx
        test    al,0FFh
        jz      peerr1                          ; pop off stack and exit

        sub     edx,8*4
        mov     [esi].RiEsp,edx

        push    ebp
        push    esp                         ; Pass current Esp to handler
        push    offset perr1_fault           ; Set Handler address
        push    PCR[PcExceptionList]        ; Set next pointer
        mov     PCR[PcExceptionList],esp    ; Link us on

        mov     eax, [esp+4*4]
        mov     [ebx+edx+28], eax
        mov     eax, [esp+4*5]
        mov     [ebx+edx+24], eax

        pop     PCR[PcExceptionList]        ; Remove our exception handle

        add     esp, 8                      ; clear stack
        pop     ebp
        lea     esp, [esp+8]                ; drop ss etc

        mov     eax,[esi].RiEFlags
        push    ecx
        call    GetVirtualBits
        pop     ecx

        push    ebp
        push    esp                         ; Pass current Esp to handler
        push    offset perr_fault           ; Set Handler address
        push    PCR[PcExceptionList]        ; Set next pointer
        mov     PCR[PcExceptionList],esp    ; Link us on

        mov     [ebx+edx+20],eax                ; push flags
        movzx   eax,word ptr [esi].RiSegCs
        mov     [ebx+edx+16],eax                ; push cs
        mov     eax,[esi].RiEip
        mov     [ebx+edx+12],eax                ; push ip
        mov     eax,RI.RiTrapFrame
        mov     eax,[eax].TsErrCode
        mov     [ebx+edx+8],eax                 ; push error code
        mov     eax,[edi].VpDosxFaultIretD
        shr     eax, 16
        mov     [ebx+edx+4],eax                 ; push iret seg
        mov     eax,[edi].VpDosxFaultIretD
        and     eax, 0ffffh
        mov     [ebx+edx],eax                   ; push iret offset

        pop     PCR[PcExceptionList]        ; Remove our exception handle

        add     esp, 8                      ; clear stack
        pop     ebp

pe50:
        call    GetHandlerAddress
        test    al,0FFh
        jz      peerr

pe60:   push    ecx
        movzx   eax,word ptr [esi].RiSegCs
        call    CsToLinear                      ; uses eax as selector
        pop     ecx
        test    al,0FFh
        jz      peerr

        mov     eax,[esi].RiEip
        cmp     eax,[esi].RiCsLimit
        ja      peerr

        mov     eax,VDM_FAULT_HANDLER_SIZE
        push    edx
        mul     ecx
        pop     edx

        push    ebp
        push    esp                         ; Pass current Esp to handler
        push    offset perr_fault           ; Set Handler address
        push    PCR[PcExceptionList]        ; Set next pointer
        mov     PCR[PcExceptionList],esp    ; Link us on

        mov     edi,PCR[PcTeb]
        mov     edi,[edi].TbVdm

        cmp     edi, _MmUserProbeAddress
        jb      @f
        mov     edi, _MmUserProbeAddress
@@:     mov     edi,[edi].VtFaultTable
        add     edi,eax
        cmp     edi, _MmUserProbeAddress
        jb      @f
        mov     edi, _MmUserProbeAddress
@@:     mov     eax,[esi].RiEFlags  ;WARNING 16 vs 32
        test    dword ptr [edi].VfFlags,VDM_INT_INT_GATE

        pop     PCR[PcExceptionList]        ; Remove our exception handle

        lea     esp, [esp+8]                ; clear stack
        pop     ebp

        jz      pe70

        and     eax,NOT (EFLAGS_INTERRUPT_MASK OR EFLAGS_TF_MASK)
        push    eax
        xor     ebx, ebx                ;  clear prefix flags
        call    SetVirtualBits
        pop     eax
pe70:   push    ecx
        mov     ecx,eax
        call    CheckVdmFlags
        and     ecx,NOT EFLAGS_TF_MASK
        mov     [esi].RiEFlags,ecx
        pop     ecx
        mov     eax,1                   ; success
pe80:   pop     esi
        pop     edi
        pop     ebx
        ret

peerr1: add     esp, 8                  ;throw away esp, ss
peerr:  xor     eax,eax
        jmp     pe80

PushException endp

perr1_fault proc
        mov     esp, [esp+8]                ; (esp)-> ExceptionList
        pop     PCR[PcExceptionList]        ; Remove our exception handle
        add     esp, 8                      ; clear stack
        pop     ebp
        jmp     peerr1
perr1_fault endp	

perr_fault proc
        mov     esp, [esp+8]                ; (esp)-> ExceptionList
        pop     PCR[PcExceptionList]        ; Remove our exception handle
        add     esp, 8                      ; clear stack
        pop     ebp
        jmp     peerr
perr_fault endp	

_PAGE   ends


_TEXT$00   SEGMENT DWORD PUBLIC 'CODE'
        ASSUME DS:NOTHING, ES:NOTHING, SS:FLAT, FS:NOTHING, GS:NOTHING

;
; Non-pageable code
;

        page   ,132
        subttl "Ipi worker for enabling Pentium extensions"
;++
;
;   Routine Description:
;
;       This routine sets or resets the VME bit in CR4 for each processor
;
;   Arguments:
;
;       [esp + 4] -- 1 if VME is to be set, 0 if it is to be reset
;   Returns:
;
;       0
;
cPublicProc _Ki386VdmEnablePentiumExtentions, 1

Enable equ [ebp + 8]
        push    ebp
        mov     ebp,esp
;
;       Ensure we do not get an interrupt in here.  We may
;       be called at IPI_LEVEL - 1 by KiIpiGenericCall.
;
        pushf
        cli

;       mov     eax,cr4
        db      0fh, 020h,0e0h

        test    Enable,1
        je      vepe20

        or      eax,CR4_VME
        jmp     vepe30

vepe20: and     eax,NOT CR4_VME

;       mov     cr4,eax
vepe30: db      0fh,022h,0e0h

        popf
        xor     eax,eax

        mov     esp,ebp
        pop     ebp
        stdRET _Ki386VdmEnablePentiumExtentions
stdENDP _Ki386VdmEnablePentiumExtentions

_TEXT$00 ends
        end

